package com.seleniti.enpareja.controller.ui.view;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;

import org.ioc.commons.flowcontrol.actioncontroller.ActionController;
import org.ioc.commons.flowcontrol.actioncontroller.ActionHandler;
import org.ioc.commons.flowcontrol.common.impl.ReceiverAdapter;
import org.ioc.commons.flowcontrol.eventbus.EventBus;
import org.ioc.commons.flowcontrol.logincontroller.IsLoggedUserInfoConsumer;
import org.ioc.commons.flowcontrol.messageboxfactory.MessageBoxFactory.PromptResponse;
import org.ioc.commons.flowcontrol.operationmanager.OperationManager;
import org.ioc.commons.flowcontrol.operationmanager.impl.OperationHandlerFactory;
import org.ioc.commons.model.globaldata.GlobalDataController.Consumer;
import org.ioc.commons.model.globaldata.IsGlobalDataConsumer;
import org.ioc.commons.utils.Format;
import org.ioc.commons.utils.FormatterLogger;
import org.ioc.commons.utils.ObjectsCompat;

import com.seleniti.enpareja.controller.common.GlobalData;
import com.seleniti.enpareja.controller.common.domain.LoggedUserInfo;
import com.seleniti.enpareja.controller.model.integration.database.domain.Couple;
import com.seleniti.enpareja.controller.model.integration.database.domain.CoupleType;
import com.seleniti.enpareja.controller.model.integration.database.domain.NotificationType;
import com.seleniti.enpareja.controller.model.integration.database.domain.ShoppingListElement;
import com.seleniti.enpareja.controller.ui.base.EnParejaViewBasePresenter;
import com.seleniti.enpareja.controller.ui.main.AppShell;
import com.seleniti.enpareja.controller.ui.view.ShoppingListView.Action;
import com.seleniti.enpareja.controller.ui.view.ShoppingListView.ActionMode;
import com.seleniti.enpareja.controller.ui.view.ShoppingListView.Board;
import com.seleniti.enpareja.controller.ui.view.ShoppingListView.Event;
import com.seleniti.enpareja.controller.ui.view.ShoppingListView.Operation;

/**
 * Presenter for the Login view
 * 
 * @author Jesús Lunar Pérez
 * 
 */
public class ShoppingListViewPresenter
		extends
		EnParejaViewBasePresenter<ShoppingListView, ShoppingListView.Event, ShoppingListView.Operation, ShoppingListView.Action>
		implements ShoppingListView.Presenter, IsLoggedUserInfoConsumer<LoggedUserInfo>,
		IsGlobalDataConsumer<GlobalData> {

	private static final FormatterLogger logger = FormatterLogger.getLogger(ShoppingListViewPresenter.class);

	protected static final int LIST_SIZE_LIMIT_TO_SHOW_LOAD_BASICS = 8;

	private static final String[] REQUIRED_PROPERTIES = { "shoppingListElements", "pendingShoppingListElements" };

	private Consumer<GlobalData> consumer;
	private String scrollToTextOnRefresh;
	protected boolean refreshedAtLeastOnce;

	private boolean amountChanged;

	public ShoppingListViewPresenter(ShoppingListView display, EventBus<Event> eventbus,
			OperationManager<Operation> operationManager, ActionController<Action> actionController) {
		super(display, eventbus, operationManager, actionController);
		this.consumer = this.globalDataController.registerGlobalDataConsumer(this);
	}

	@Override
	public void onInitDisplay() {
		super.onInitDisplay();

		display.setActionMode(ActionMode.SWITCH_LIST);
		shell.loadBasicsButtonText().setText("Productos básicos");

		this.consumer.requires(GlobalData.UserAgent);
	}

	@Override
	protected void bindComponents() {
		super.bindComponents();
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(display));
		operationManager.bindOperationsRunning(OperationHandlerFactory.<Operation> forLoaders(shell.globalLoader()));

		shell.actionController().bindAction(AppShell.Action.DeleteNow, new ActionHandler<AppShell.Action>() {

			@Override
			public void handleAction(Object sender, com.seleniti.enpareja.controller.ui.main.AppShell.Action action,
					Object... params) {
				deleteSelected();
			}
		});

		shell.actionController().bindAction(AppShell.Action.LoadBasics, new ActionHandler<AppShell.Action>() {

			@Override
			public void handleAction(Object sender, com.seleniti.enpareja.controller.ui.main.AppShell.Action action,
					Object... params) {
				display.showBasics();
			}
		});
	}

	protected void deleteSelected() {
		if (display.getActionMode() == ActionMode.DELETE_ELEMENT) {
			Set<ShoppingListElement> selection = display.getSelection();

//			if (!ensureCouple("Eliminar un elemento en la lista", Operation.GettingData, operationManager,
//					REQUIRED_PROPERTIES)) {
//				return;
//			}

			Couple couple = display.couple().getValue();

			if (couple.getShoppingListElements() != null) {
				couple.getShoppingListElements().removeAll(selection);
				saveCouple(Operation.Saving, operationManager, true);
				display.setActionMode(ActionMode.SWITCH_LIST);
			}
		}

	}

	@Override
	public void onActive() {
		super.onActive();

		shell.refreshButtonVisibility().setVisible(true);

		display.boardShown().setValue(Board.SHOPPING_LIST);
		tryToShowBasicsList();

		this.loginController.requiresLoggedUserInfoIfDisplayActive(this);
		this.consumer.ensureRequirements();
	}

	@Override
	public void onDeactive() {
		super.onDeactive();

		shell.loadBasicsButtonVisibility().setVisible(false);

		ensureAmountSaved();
	}

	@Override
	public void onRequiredGlobalDataReady() {
		showGoBackButtonIfRequired();
	}

	@Override
	public void onRequiredGlobalDataNotReadyYet() {
		/*
		 * Nothing to do
		 */
	}

	@Override
	public void onRequiredGlobalDataUpdated(GlobalData globalDataUpdated) {
		showGoBackButtonIfRequired();
	}

	@Override
	protected void refreshData() {
		if (!this.operationManager.isOpPending(Operation.GettingData)) {

			super.refreshCouple(new ReceiverAdapter<Couple>() {

				@Override
				public void success(Couple result) {
					display.shoppingList().clear();
					display.weDontHaveCounter().setValue(
							result != null ? result.getPendingShoppingListElements() : null);
					if (result != null && result.getShoppingListElements() != null) {
						if (!Boolean.TRUE.equals(display.weDontHaveFilter().getValue())) {
							display.shoppingList().addAll(result.getShoppingListElements());
						} else {
							for (ShoppingListElement element : result.getShoppingListElements()) {
								if (!element.isWeHave()) {
									display.shoppingList().add(element);
								}
							}
						}
					}

					if (scrollToTextOnRefresh != null) {
						int idx = indexOfElement(scrollToTextOnRefresh);
						if (idx >= 0) {
							display.scrollToElement(idx);
						}
						scrollToTextOnRefresh = null;
					}

					ShoppingListViewPresenter.this.refreshedAtLeastOnce = true;
					tryToShowBasicsList();
				}
			}, Operation.GettingData, operationManager, REQUIRED_PROPERTIES);
		} else {
			logger.info("Already getting data. We do not call it again.");
		}
	}

	private int indexOfElement(String text) {
		for (int i = 0; i < display.shoppingList().size(); i++) {
			ShoppingListElement elm = display.shoppingList().get(i);
			if (elm != null && ObjectsCompat.equals(elm.getText(), text)) {
				return i;
			}
		}
		return -1;
	}

	private void refreshLoggedUserInfo() {
		LoggedUserInfo lui = this.loginController.getLoggedUserInfo();

		display.setEnabled(lui != null && lui.getCoupleName() != null && !lui.getCoupleName().isEmpty());

		shell.nonAssociatedPanelVisibility()
				.setVisible(
						lui != null
								&& lui.isLogged()
								&& (lui.getCoupleName() == null || lui.getCoupleName().isEmpty() || lui.getCoupleType() == CoupleType.TESTING_MODE_COUPLE));

		shell.firstStepsVisibility().setVisible(
				shell.nonAssociatedPanelVisibility().isVisible()
						&& lui.getCoupleType() != CoupleType.TESTING_MODE_COUPLE);
		shell.nonAssociatedButtonVisibility().setVisible(!shell.firstStepsVisibility().isVisible());
	}

	@Override
	public void onLoggedUserInfoDataReady() {
		LoggedUserInfo loggedUserInfo = this.loginController.getLoggedUserInfo();

		if (!ObjectsCompat.equals(loggedUserInfo, display.loggedUserInfo().getValue())) {

			this.display.loggedUserInfo().setValue(loggedUserInfo);

			refreshData();
			refreshLoggedUserInfo();
		}
	}

	@Override
	public void onLoggedUserInfoDataUpdated(LoggedUserInfo loggedUserInfo) {
		refreshLoggedUserInfo();
	}

	@Override
	public void onLoggedUserInfoNotReadyYet() {
	}

	@Override
	public void onSwitchElementFromList(final ShoppingListElement value) {
		value.setWeHave(!value.isWeHave());
		logger.info("Switch element from list. Now {0} {1}", value.isWeHave() ? "we have" : "we dont have",
				value.getText());
		saveCouple(Operation.Saving, operationManager, true, NotificationType.SHOPPING_LIST_CHANGED);
	}

	@Override
	public void onAddNewElement() {
		logger.info("Adding new element");
		messageBoxFactory.prompt("Nuevo en la lista", "Escribe el nombre del producto que quieres añadir", "",
				new PromptResponse() {

					@Override
					public void onUserResponse(boolean userResponse, String response) {
						if (userResponse && response != null && !response.isEmpty()) {

							String text = Format.capitalize(response);

							addNewElement(text, true);

							scrollToTextOnRefresh = text;
						}

					}
				});
	}

	@Override
	public void onAddSelectedBasicElements() {
		logger.info("Adding {0} selected basic elements", display.selectedBasicProducts().size());

		if (!display.selectedBasicProducts().isEmpty()) {
			for (String basicProduct : display.selectedBasicProducts()) {
				addNewElement(basicProduct, false);
			}

			saveCouple(Operation.Saving, operationManager, true);
		}

		display.boardShown().setValue(Board.SHOPPING_LIST);
		tryToShowBasicsList();
	}

	@Override
	public void onCancelAddingSelectedBasicElements() {
		display.selectedBasicProducts().clear();
		display.boardShown().setValue(Board.SHOPPING_LIST);
		tryToShowBasicsList();
	}

	@Override
	public void onActionModeChanged() {
		ActionMode am = display.getActionMode();

		switch (am) {
		case DELETE_ELEMENT:
			this.shell.deleteButtonVisibility().setVisible(true);
			this.shell.loadBasicsButtonVisibility().setVisible(false);
			break;
		default:
			this.shell.deleteButtonVisibility().setVisible(false);
			tryToShowBasicsList();
		}

		ensureAmountSaved();
	}

	protected void ensureAmountSaved() {
		if (this.amountChanged) {
			saveCouple(Operation.Saving, operationManager, false);
			this.amountChanged = false;
		}
	}

	@Override
	public void onSelectionChanged() {
		ActionMode am = display.getActionMode();

		if (am == ActionMode.RENAME_ELEMENT) {
			Set<ShoppingListElement> selection = display.getSelection();

			if (!selection.isEmpty()) {
				ShoppingListElement single = selection.iterator().next();

				display.renameElement(single);
			}
		}

	}

	@Override
	public void onRenameElement(final ShoppingListElement element) {
		messageBoxFactory.prompt("Cambiar", "Escribe el nuevo nombre del producto", element.getText(),
				new PromptResponse() {

					@Override
					public void onUserResponse(boolean userResponse, String response) {
						if (userResponse && response != null && !response.isEmpty()) {

							String text = Format.capitalize(response);
							element.setText(text);

							scrollToTextOnRefresh = text;

							saveCouple(Operation.Saving, operationManager, true);

							display.setActionMode(ActionMode.SWITCH_LIST);
						}

					}
				});
	}

	@Override
	public void onWeHaveFilterChanged() {
		refreshData();
	}

	@Override
	public void onRefreshData() {
		refreshData();
	}

	@Override
	public void onModifyButtonToggled(boolean enabled) {
		display.setActionMode(enabled ? ActionMode.RENAME_ELEMENT : ActionMode.SWITCH_LIST);
	}

	@Override
	public void onSetAmountButtonToggled(boolean enabled) {
		display.setActionMode(enabled ? ActionMode.SET_AMOUNT : ActionMode.SWITCH_LIST);
	}

	@Override
	public void onAmountChanged(ShoppingListElement element, Integer amount) {
		element.setAmount(amount != null && amount > 0 ? amount : null);
		this.amountChanged = true;
	}

	private final static String[] BASIC_PRODUCTS = { "Aceite de oliva", "Aceite de girasol", "Aceitunas", "Ajos",
			"Avecrem", "Café", "Cereales", "Chacinas", "Chocolate", "Colacao", "Ensalda", "Espaguettis", "Harina",
			"Ketchup", "Lechuga", "Mayonesa", "Mejillones", "Papel higiénico", "Paté", "Pimientos", "Arroz", "Atún",
			"Azucar", "Bolsas de basura", "Cebollas", "Cervezas", "Champú", "Coca-cola", "Desodorante hombre",
			"Desodorante mujer", "Lavaplatos", "Flamenquines", "Fregasueltos", "Gel de baño", "Gomina", "Hamburguesas",
			"Huevos", "Jamón", "Jamón de york", "Latas de refrescos", "Leche", "Mantequilla", "Merluza",
			"Nata para cocinar", "Pan", "Pan de molde", "Pan de hamburguesa", "Pasta de dientes",
			"Pastillas lavavajillas", "Patatas", "Pepsi", "Pizzas", "Queso", "Queso fresco", "Rollo de cocina",
			"Sacarina", "Sal", "Salchichas", "Salchichón", "Servilletas", "Suavizante", "Detergente", "Tomate frito",
			"Tomates", "Tranchetes", "Vino para cocinar", "Yogures", "Zumos" };

	@Override
	public void onShowBasics() {
		loadAndBasics();
	}

	protected void loadAndBasics() {
		Couple couple = display.couple().getValue();
		if (couple != null) {
			if (display.basicProducts().isEmpty()) {

				display.basicProducts().addAll(Arrays.asList(BASIC_PRODUCTS));

				Collections.sort(display.basicProducts(), new Comparator<String>() {

					@Override
					public int compare(String o1, String o2) {
						return o1.compareTo(o2);
					}
				});
			}

			display.selectedBasicProducts().clear();
			display.boardShown().setValue(Board.BASIC_PRODUCTS_LIST);
			shell.loadBasicsButtonVisibility().setVisible(false);
		} else {
			logger.warn("Required couple for loading basic products not found");
			messageBoxFactory.alert("Algo no va bien", "Parece que no puedes cargar la lista de productos básicos.");
		}
	}

	protected void addNewElement(String text, boolean save) {

//		if (!ensureCouple("Añadir un elemento en la lista", Operation.GettingData, operationManager,
//				REQUIRED_PROPERTIES)) {
//			return;
//		}

		Couple couple = display.couple().getValue();

		/**
		 * Ensure we do not have an empty list
		 */
		if (couple.getShoppingListElements() == null) {
			couple.setShoppingListElements(new ArrayList<ShoppingListElement>());
		}

		/**
		 * In case user is adding a element which previously exists, just modify
		 * it.
		 */
		boolean exists = false;
		for (ShoppingListElement old : couple.getShoppingListElements()) {
			if (text.equals(old.getText())) {
				old.setWeHave(false);
				if (save) {
					saveCouple(Operation.Saving, operationManager, true);
				}
				exists = true;
				break;
			}
		}

		if (!exists) {
			ShoppingListElement sle = new ShoppingListElement();
			sle.setText(text);
			sle.setCouple(couple);

			couple.getShoppingListElements().add(sle);
		}

		if (save) {
			saveCouple(Operation.Saving, operationManager, true);
		}
	}

	protected void tryToShowBasicsList() {
		if ((ShoppingListViewPresenter.this.refreshedAtLeastOnce || !display.shoppingList().isEmpty())
				&& !Boolean.TRUE.equals(display.weDontHaveFilter().getValue())) {
			shell.loadBasicsButtonVisibility().setVisible(
					!display.operationManager().isBusy()
							&& display.shoppingList().size() < LIST_SIZE_LIMIT_TO_SHOW_LOAD_BASICS);
		}
	}

	@Override
	public boolean onGoBoardBack() {
		Board curBoard = display.boardShown().getValue();

		if (curBoard != null && curBoard != Board.SHOPPING_LIST) {
			display.boardShown().setValue(Board.SHOPPING_LIST);
			return true;
		}

		return false;
	}

}
